/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024.
 * Description: RTOS pac permissive mode code
 * Author: wanghuizhao
 * Create: 2024-07-14
 */

#define FPAC_ERR_CODE_MASK 0x3
#define AKEY_ERR_CODE 0x0
#define BKEY_ERR_CODE 0x1

#include <linux/kernel.h>
#include <linux/rtos/cfi_permissive_mode.h>
#include <linux/sched/task.h>
#include <asm/ptrace.h>
#include <asm/pointer_auth.h>
#include <asm/insn.h>

// The permissive mode needs to be enabled by default for wireless products.
static bool pac_permissive_enabled = true;

bool get_pac_permissive_enabled(void)
{
	return pac_permissive_enabled;
}
EXPORT_SYMBOL_NS(get_pac_permissive_enabled, HW_RTOS_NS);

static int __init pac_permissive_setup(char *str)
{
	int val = 0;

	if (!get_option(&str, &val))
		return 0;
	if (val == 0)
		pac_permissive_enabled = false;

	return 1;
}
__setup("pac_permissive=", pac_permissive_setup);

bool fpac_fault_repair(struct pt_regs *regs, unsigned long esr)
{
	u32 *pc = (u32 *)regs->pc;
	u32 rd, value;
	bool is_print;
	unsigned long addr = 0;
	u32 insn = le32_to_cpu(*pc);

	if (!get_pac_permissive_enabled())
		return false;

	// esr[1:0] pac error code
	// esr[1] data pac error is 1, ins pac error is 0
	// esr[0] akey is 0, bkey is 1
	value = esr & FPAC_ERR_CODE_MASK;
	if (value == AKEY_ERR_CODE || value == BKEY_ERR_CODE) {
		if (aarch64_insn_is_autiasp(insn))
			rd = REG_LR_NUM;
		else
			rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, insn);
		addr = pt_regs_read_reg(regs, rd);
		if (pac_check_bit(addr)) {
			addr = ptrauth_strip_insn_pac(addr);
			pt_regs_write_reg(regs, rd, addr);
		}
		regs->pc += AARCH64_INSN_SIZE;
		is_print = rtos_pac_logger(current->comm, current->pid, addr, regs->pc);
		if (is_print && value == AKEY_ERR_CODE)
			pr_warn("comm:[%s] FPAC exception backward check failed\n", current->comm);
		if (is_print && value == BKEY_ERR_CODE)
			pr_warn("comm:[%s] FPAC exception forward check failed\n", current->comm);
		return true;
	}

	return false;
}

